﻿using System.Collections.Generic;
using System.Linq;

using VirtualTournaments.DataAccess;
using VirtualTournaments.DataAccess.EntityManagers;
using System;
using VirtualTournaments.Business.Bets;
using VirtualTournaments.Business.Tournaments.Exceptions;

namespace VirtualTournaments.Services.Tournaments
{
	public class TournamentsService
	{

		/// <summary>
		/// devuelve los partidos de la proxima fecha
		/// </summary>
		/// <param name="tournament"></param>
		/// <returns></returns>
		public static Match[] GetTournamentNextJourneyMatches(Services.Tournaments.Tournament tournament)
		{
			ITournamentsManager tournamentData = DataAccessManager.EntityManagersFactory.GetTournamentsManager();
			Business.Tournaments.Tournament bizTournament = tournamentData.GetByExactName(tournament.Name);
			
			List<Match> result = new List<Match>();
			try { 
				Business.Tournaments.Journey bizJourney = bizTournament.GetNextJourney();

				foreach (var match in bizJourney)
				{
					result.Add(Bussiness2ServiceConversor.ConvertMatch(match));
				}
			}
			catch (FinishedTournamentException)
			{
			}
			
			return result.ToArray();
		}

		/// <summary>
		/// devuelve los partidos de la fecha anterior que fueron jugados, con sus resultados
		/// </summary>
		/// <param name="tournament"></param>
		/// <returns></returns>
		public static Match[] GetTournamentLastJourneyMatches(Services.Tournaments.Tournament tournament)
		{
			ITournamentsManager tournamentData = DataAccessManager.EntityManagersFactory.GetTournamentsManager();
			Business.Tournaments.Tournament bizTournament = tournamentData.GetByExactName(tournament.Name);
			List<Match> result = new List<Match>();
			try
			{ 
				Business.Tournaments.Journey bizJourney = bizTournament.GetLastJourney();
				foreach (var match in bizJourney)
				{
					if (match.IsPlayed)
					{
						result.Add(Bussiness2ServiceConversor.ConvertMatch(match));
					}
				}
			}
			catch (NotStartedTournamentException)
			{
				//no hago nada, devuelvo un array vacío mas abajo
			}
			return result.ToArray();
		}

		/// <summary>
		/// Crea un torneo del tipo indicado en la propiedad Type del parametro tournament
		/// Registra el torneo y los partidos de la primer fecha para realizar apuestas sobre ellos.
		/// </summary>
		/// <param name="teams"></param>
		/// <param name="tournament"></param>
		public static void CreateTournament(Team[] teams, Tournament tournament)
		{
			ITournamentsManager dataManager = DataAccessManager.EntityManagersFactory.GetTournamentsManager();

			Business.Tournaments.Tournament newTournament = CreateBizTournament(teams, tournament);
			try
			{
				dataManager.Save(newTournament);
			}
			catch (VirtualTournaments.DataAccess.Exceptions.DataAccessException ex)
			{
				throw new ServiceException("No se puede crear el torneo: " + ex.ToString());
			}

			Services.Bets.BetsService.RegisterTournament(newTournament);
			try { 
				Services.Bets.BetsService.RegisterJourneysMatches(newTournament.GetNextJourney());
			}
			catch (FinishedTournamentException)
			{
				//en caso de torneo finalizado no hay partidos para apostar
			}
			
		}

		/// <summary>
		/// Factory method de Tournament
		/// </summary>
		/// <param name="teams"></param>
		/// <param name="tournament"></param>
		/// <returns></returns>
		private static Business.Tournaments.Tournament CreateBizTournament(Team[] teams, Tournament tournament)
		{
			Business.Tournaments.Tournament newTournament;
			if (tournament.Type == Tournament.ALLAGAINSTALL_TYPE)
			{
				newTournament = new VirtualTournaments.Business.Tournaments.AllAgainstAllTournament(tournament.Name, Service2BussinessConversor.convertTeams(teams));
			}
			else if (tournament.Type == Tournament.PLAYOFF_TYPE)
			{
				newTournament = new VirtualTournaments.Business.Tournaments.PlayOffTournament(tournament.Name, Service2BussinessConversor.convertTeams(teams));
			}
			else
			{
				throw new ServiceException("Tipo de torneo desconocido");
			}
			return newTournament;
		}

		/// <summary>
		/// Juega la siguiente jornada de un torneo en especial, ademas:
		/// *liquida las apuestas de partidos y si finalizó el torneo, las correspondientes a este
		/// *registra los partidos de la proxima jornada para poder apostar sobre ellos
		/// </summary>
		/// <returns>
		/// devuelve true si el torneo continua, false si terminó
		/// </returns>
		public static bool PlayNextJourney(Tournament tournament)
		{
			ITournamentsManager dataManager = DataAccessManager.EntityManagersFactory.GetTournamentsManager();

			Business.Tournaments.Tournament bizTournament = dataManager.GetByExactName(tournament.Name);
			bizTournament.PlayNextJourney();
			dataManager.Save(bizTournament);

			SettleBets(bizTournament);

			try
			{
				Services.Bets.BetsService.RegisterJourneysMatches(bizTournament.GetNextJourney());
			}
			catch (FinishedTournamentException)
			{
				//en caso de torneo finalizado no hay partidos para registrar
			}
			return !(bizTournament.IsFinished);
		}

		private static void SettleBets(Business.Tournaments.Tournament bizTournament)
		{
			try
			{
				Services.Bets.BetsService.NotifyFinishedMatches(bizTournament.GetLastJourney().ToArray());
			}
			catch (NotStartedTournamentException)
			{
				//en caso de que el torneo no tenga la primer fecha jugada no hay que liquidar ningun partido
			}
			if (bizTournament.IsFinished)
			{
				Services.Bets.BetsService.NotifyFinishedTournament(bizTournament);
			}
		}

		public static Tournament[] GetAllTournaments()
		{
			List<Tournament> result = new List<Tournament>();
			ITournamentsManager tournamentsManager = DataAccessManager.EntityManagersFactory.GetTournamentsManager();
			tournamentsManager.GetAll().ToList().ForEach(t => result.Add(Bussiness2ServiceConversor.ConvertTournament(t)));
			return result.ToArray();
		}

		public static Tournament GetTournamentByName(string name)
		{
			ITournamentsManager tournamentsManager = DataAccessManager.EntityManagersFactory.GetTournamentsManager();

			Business.Tournaments.Tournament bizTournament = tournamentsManager.GetByExactName(name);
			if (bizTournament == null)
			{
				throw new Services.ServiceException("No se encuentra el torneo con nombre: " + name);
			}
			return Bussiness2ServiceConversor.ConvertTournament(bizTournament);
		}

		public static Tournament[] GetActiveTournaments()
		{
			List<Tournament> result = new List<Tournament>();
			ITournamentsManager tournamentsManager = DataAccessManager.EntityManagersFactory.GetTournamentsManager();
			tournamentsManager.GetActives().ToList().ForEach(t => result.Add(Bussiness2ServiceConversor.ConvertTournament(t)));
			return result.ToArray();
		}

		private static bool checkAvailableStandingsForTournament(Business.Tournaments.Tournament tournament)
		{
			if (typeof(Business.Tournaments.AllAgainstAllTournament).IsInstanceOfType(tournament))
				return true;
			else return false;

		}
		public static Standings GetStandingsByTournament(string name)
		{
			ITournamentsManager tournamentsManager = DataAccessManager.EntityManagersFactory.GetTournamentsManager();
			Business.Tournaments.Tournament businessTournament = tournamentsManager.GetByExactName(name);

			if (checkAvailableStandingsForTournament(businessTournament))
				return Bussiness2ServiceConversor.ConvertStandings(((Business.Tournaments.AllAgainstAllTournament)tournamentsManager.GetByExactName(name)).Standings);
			else
				throw new ServiceException("No hay tabla de posiciones para los torneos de tipo " + Tournament.PLAYOFF_TYPE);

		}
	}
}
